
#include "OCIStmt.h"
#include "./odpi/include/dpi.h"
#include <sstream>


OCIStmt::OCIStmt(dpiConn* conn)
    : conn_(conn)
    , stmt_(nullptr)
{

}

OCIStmt::~OCIStmt()
{
    if (stmt_) {
        dpiStmt_release(stmt_);
    }
}

bool OCIStmt::perpare(const std::string& sql)
{
    if (stmt_) {
        dpiStmt_release(stmt_);
    }
    if (dpiConn_prepareStmt(conn_, 0, sql.c_str(), sql.size(), nullptr, 0, &stmt_) < 0) {
        return false;
    }
    return true;
}

int64_t OCIStmt::exec()
{
    if (dpiStmt_execute(stmt_, DPI_MODE_EXEC_DEFAULT, nullptr) < 0) {
        return -1;
    }
    uint64_t row_cnt = 0;
    dpiStmt_getRowCount(stmt_, &row_cnt);
    return (int64_t)row_cnt;
}

bool OCIStmt::query(DBRows& rows)
{
    uint32_t cols = 0;
    if (dpiStmt_execute(stmt_, DPI_MODE_EXEC_DEFAULT, &cols) < 0) {
        return false;
    }

    // meta data
    std::vector<std::shared_ptr<row_meta_data>> fields;
    for (uint32_t i = 0; i < cols; ++i)
    {
        dpiQueryInfo field = {};
        if (dpiStmt_getQueryInfo(stmt_, i + 1, &field) < 0) {
            return false;
        }
        auto m = std::make_shared<row_meta_data>();
        m->col_index = fields.size();
        m->name.append(field.name, field.nameLength);
        m->origin_name = m->name;
        m->origin_type = field.typeInfo.oracleTypeNum;
        
        fields.emplace_back(m);
    }

    int found = 0;
    while(true)
    {
        uint32_t row_idx = 0;
        if (dpiStmt_fetch(stmt_, &found, &row_idx) < 0) {
            return false;
        }
        if (!found) {
            break;
        }
        DBRow row;
        for (uint32_t i = 0; i < cols; ++i)
        {
            dpiNativeTypeNum type;
            dpiData *qd = nullptr;
            if (dpiStmt_getQueryValue(stmt_, i + 1, &type, &qd) < 0) {
                return false;
            }
            std::string str;
            switch (type)
            {
            case DPI_NATIVE_TYPE_BOOLEAN:
                str = std::to_string(qd->value.asBoolean);
                break;
            case DPI_NATIVE_TYPE_INT64:
                str = std::to_string(qd->value.asInt64);
                break;
            case DPI_NATIVE_TYPE_UINT64:
                str = std::to_string(qd->value.asUint64);
                break;
            case DPI_NATIVE_TYPE_FLOAT:
                str = std::to_string(qd->value.asFloat);
                break;
            case DPI_NATIVE_TYPE_DOUBLE:
                str = std::to_string(qd->value.asDouble);
                break;
            case DPI_NATIVE_TYPE_BYTES:
                str.append(qd->value.asBytes.ptr, qd->value.asBytes.length);
                break;
            case DPI_NATIVE_TYPE_TIMESTAMP:
            {
                char buf[256] = {};
                snprintf(buf, sizeof(buf), "%4d-%.2d-%.2d %.2d:%.2d:%.2d.%.6d+%.2d:%.2d",
                    qd->value.asTimestamp.year, qd->value.asTimestamp.month, qd->value.asTimestamp.day,
                    qd->value.asTimestamp.hour, qd->value.asTimestamp.minute, qd->value.asTimestamp.second,
                    qd->value.asTimestamp.fsecond, qd->value.asTimestamp.tzHourOffset, qd->value.asTimestamp.tzMinuteOffset);
                str = buf;
            }
            break;
            case DPI_NATIVE_TYPE_INTERVAL_DS:
            {
                char buf[256] = {};
                snprintf(buf, sizeof(buf), "%.2d %.2d:%.2d:%.2d.%.6d",
                    qd->value.asIntervalDS.days,qd->value.asIntervalDS.hours,
                    qd->value.asIntervalDS.minutes, qd->value.asIntervalDS.seconds,
                    qd->value.asIntervalDS.fseconds);
                str = buf;
            }
            break;
            case DPI_NATIVE_TYPE_LOB:
            {
                uint64_t size = 0, valen = 0;
                if (dpiLob_getSize(qd->value.asLOB, &size) < 0) {
                    return false;
                }
                char *buf = new char[size];
                dpiLob_readBytes(qd->value.asLOB, 0, size, buf, &valen);
                str.append(buf, valen);
                delete[] buf;
            }
            break;
            case DPI_NATIVE_TYPE_INTERVAL_YM:
            {
                char buf[256] = {};
                snprintf(buf, sizeof(buf), "%.4d-%.2d",qd->value.asIntervalYM.years, qd->value.asIntervalYM.months);
                str = buf;
            }
            break;
            default:
                printf("Unsupported types %d\n", type);
                break;
            }
            row.row_.emplace_back(DBValue(fields[i], str));
        }
        rows.rows_.emplace_back(row);
    }
    return true;
}

bool OCIStmt::bind(int index, std::nullptr_t)
{
    dpiData bd;
    bd.isNull = 1;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_INT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, bool val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asBoolean = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_BOOLEAN, &bd) >= 0;
}

bool OCIStmt::bind(int index, int8_t val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asInt64 = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_INT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, int16_t val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asInt64 = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_INT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, int32_t val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asInt64 = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_INT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, int64_t val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asInt64 = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_INT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, uint8_t val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asUint64 = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_UINT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, uint16_t val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asUint64 = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_UINT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, uint32_t val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asUint64 = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_UINT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, uint64_t val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asUint64 = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_UINT64, &bd) >= 0;
}

bool OCIStmt::bind(int index, float val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asFloat = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_FLOAT, &bd) >= 0;
}

bool OCIStmt::bind(int index, double val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asDouble = val;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_DOUBLE, &bd) >= 0;
}

bool OCIStmt::bind(int index, const std::string& val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asBytes.encoding = nullptr;
    bd.value.asBytes.ptr = (char*)val.data();
    bd.value.asBytes.length = val.size();
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_BYTES, &bd) >= 0;
}

bool OCIStmt::bind(int index, const char* val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asBytes.encoding = nullptr;
    bd.value.asBytes.ptr = (char*)val;
    bd.value.asBytes.length = strlen(val);
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_BYTES, &bd) >= 0;
}

bool OCIStmt::bind(int index, const unsigned char* val)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asBytes.encoding = nullptr;
    bd.value.asBytes.ptr = (char*)val;
    bd.value.asBytes.length = strlen((char*)val);
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_BYTES, &bd) >= 0;
}

bool OCIStmt::bind(int index, const void* val, size_t len)
{
    dpiData bd;
    bd.isNull = 0;
    bd.value.asBytes.encoding = nullptr;
    bd.value.asBytes.ptr = (char*)val;
    bd.value.asBytes.length = len;
    return dpiStmt_bindValueByPos(stmt_, index, DPI_NATIVE_TYPE_BYTES, &bd) >= 0;
}

